可见性和原子性到底什么区别?「剖析volatile后你就懂了」

您所在的位置:网站首页 c volatile关键字 可见性和原子性到底什么区别?「剖析volatile后你就懂了」

可见性和原子性到底什么区别?「剖析volatile后你就懂了」

2023-05-27 20:12| 来源: 网络整理| 查看: 265

概念

volatile定义的变量,每次获取时候都从cpu主存获取,禁止从cpu缓存获取

指令重排序 public class Singleton { private volatile static Singleton uniqueInstance; private Singleton() { } public static Singleton getUniqueInstance() { //先判断对象是否已经实例过,没有实例化过才进入加锁代码 if (uniqueInstance == null) { //类对象加锁 synchronized (Singleton.class) { if (uniqueInstance == null) { uniqueInstance = new Singleton(); } } } return uniqueInstance; } }

uniqueInstance 采用 volatile 关键字修饰也是很有必要的, uniqueInstance = new Singleton(); 

这段代码其实是分为三步执行:

为 uniqueInstance 分配内存空间 初始化 uniqueInstance 将 uniqueInstance 指向分配的内存地址

但是由于 JVM 具有指令重排的特性,执行顺序有可能变成 1->3->2。指令重排在单线程环境下不会出现问题,但是在多线程环境下会导致一个线程获得还没有初始化的实例。例如,线程 T1 执行了 1 和 3,此时 T2 调用 getUniqueInstance() 后发现 uniqueInstance 不为空,因此返回 uniqueInstance,但此时 uniqueInstance 还未被初始化。

原子性不能保证 public class VolatoleAtomicityDemo { public volatile static int inc = 0; public void increase() { inc++; } public static void main(String[] args) throws InterruptedException { ExecutorService threadPool = Executors.newFixedThreadPool(5); VolatoleAtomicityDemo volatoleAtomicityDemo = new VolatoleAtomicityDemo(); for (int i = 0; i < 5; i++) { threadPool.execute(() -> { for (int j = 0; j < 500; j++) { volatoleAtomicityDemo.increase(); } }); } // 等待1.5秒,保证上面程序执行完成 Thread.sleep(1500); System.out.println(inc); threadPool.shutdown(); } }

很多人会误认为自增操作 inc++ 是原子性的,实际上,inc++ 其实是一个复合操作,包括三步:

读取 inc 的值。 对 inc 加 1。 将 inc 的值写回内存。

volatile 是无法保证这三个操作是具有原子性的,有可能导致下面这种情况出现:

线程 1 对 inc 进行读取操作之后,还未对其进行修改。线程 2 又读取了 inc的值并对其进行修改(+1),再将inc 的值写回内存。

线程 2 操作完毕后,线程 1 对 inc的值进行修改(+1),再将inc 的值写回内存。

这也就导致两个线程分别对 inc 进行了一次自增操作后,inc 实际上只增加了 1。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3